package com.ficus.road.maintain.controller;

import com.alibaba.fastjson.JSONObject;
import com.ficus.road.maintain.authority.dto.RoleDTO;
import com.ficus.road.maintain.authority.dto.UserDTO;
import com.ficus.road.maintain.authority.service.RoleService;
import com.ficus.road.maintain.authority.service.UserService;
import com.ficus.road.maintain.authority.utils.SecurityUtils;
import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.core.model.coverage.CycleNum;
import com.ficus.road.maintain.core.model.coverage.CycleNumVO;
import com.ficus.road.maintain.core.model.coverage.ManualFixRequest;
import com.ficus.road.maintain.dao.*;
import com.ficus.road.maintain.dao.coverage.CycleNumDao;
import com.ficus.road.maintain.service.CarService;
import com.ficus.road.maintain.service.CompanyService;
import com.ficus.road.maintain.service.CycleNumService;
import com.ficus.road.maintain.service.ZhonghaiClient;
import com.ficus.road.maintain.service.coverage.YTCoverageService;
import com.ficus.road.maintain.util.LngLonUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xianwen.yang
 */
@RestController
@RequiredArgsConstructor
@Slf4j
public class CompanyController {
    private final ZhonghaiClient zhonghaiClient;
    private final CompanyDao companyDao;
    private final UnitDao unitDao;
    private final ZhongHaiDao zhongHaiDao;
    private final CompanyService companyService;
    private final UserService userService;
    private final RoleService roleService;
    private final CarDao carDao;
    private final CycleNumDao cycleNumDao;
    private final CycleNumService cycleNumService;
    private final RoadInfoDao roadInfoDao;
    private final GpsHistoryDao gpsHistoryDao;
    private final CarService carService;
    private final YTCoverageService ytCoverageService;
    @Value("${coverage.gps_fix_distance}")
    private Integer gpsFixDistance;

    /**
     * 获取养护公司扣分细则
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/company/score",
            produces = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<CompanyScoreResponse> getCompanyScore(@RequestBody CompanyScoreRequest request) {
        String id = request.getId();
        String yearMonth = request.getYearMonth();
        Query query = new Query(Criteria.where("type").is(ZhonghaiClient.ZH_METHOD_UNIT_SCORE).and("arg").is(id + "-" + yearMonth));
        query.with(new Sort(Sort.Direction.DESC, "time_stamp")).skip(0).limit(1);
        ZhongHai dbData = zhongHaiDao.findOne(query);
        CompanyScoreResponse response;
        if (Objects.nonNull(dbData)) {
            response = zhonghaiClient.parseCompanyScoreJson(dbData.getData());
        } else {
            String s = zhonghaiClient.queryUnitScore(id, yearMonth);
            response = zhonghaiClient.parseCompanyScoreJson(s);
        }
        //修改格式
        if (ObjectUtils.isNotEmpty(response) && ObjectUtils.isNotEmpty(response.getScore())) {
            Double score = response.getScore();
            response.setScore(Double.parseDouble(String.format("%.2f", score)));
        }
        return new ResponseEntity<>(response, HttpStatus.OK);
    }


    /**
     * 获取某周期的标段覆盖率
     *
     * @param currentCycleNum
     * @return
     */
    @GetMapping("/unit/coverRates/{currentCycleNum}")
    public ResponseEntity<List<UnitCoverRateResponse>> getUnitCoverage(@PathVariable("currentCycleNum") String currentCycleNum,@RequestParam(required = false) String source) {
        //获取当前用户的角色
        Set<String> unitIdSet = unitDao.findAll().stream().map(Unit::getId).collect(Collectors.toSet());
//        UserDTO userDTO = userService.queryUserByUsername(SecurityUtils.getCurrentUser().getUsername());
//        Set<String> roleIdList = userDTO.getRoleIdList();
//        List<RoleDTO> roleDTOS = roleService.queryRolesById(roleIdList);
//        List<String> roleName = roleDTOS.stream().map(RoleDTO::getRoleName).collect(Collectors.toList());
        Query queryCoverage = new Query();
//        if (!roleName.contains("管理员") && !roleName.contains("道运中心") && !roleName.contains("市道运局")) {
//            queryCoverage.addCriteria(Criteria.where("car_id").in(userDTO.getCarIdList()));
//            List<Car> cars = carDao.findByQuery(new Query(Criteria.where("_id").in(userDTO.getCarIdList())));
//            if (ObjectUtils.isNotEmpty(cars)) {
//                unitIdSet = cars.stream().map(Car::getUnits).collect(Collectors.toList()).stream().flatMap(Collection::stream).collect(Collectors.toSet());
//            }
//        }
        if ("0".equals(currentCycleNum)) {
            currentCycleNum = cycleNumService.getCurrentCycleNum();
        }
        List<UnitCoverRateResponse> unitCoverage;
        if (StringUtils.equals("yitu", source)){
            unitCoverage = ytCoverageService.getUnitCoverage(unitIdSet, currentCycleNum);
        }else {
            unitCoverage = companyService.getUnitCoverage(unitIdSet, currentCycleNum);
        }

        return new ResponseEntity<>(unitCoverage, HttpStatus.OK);
    }


    /**
     * 获取养护公司所属标段覆盖率
     *
     * @param companyId
     * @return
     */
    @GetMapping("/company/coverRate/{companyId}")
    public ResponseEntity<List<CompanyCoverRateInfoResponse>> getCompanyCoverRateInfo(@PathVariable("companyId") String companyId, @RequestParam(required = false) String source) {
        String currentCycleNum = cycleNumService.getCurrentCycleNum();
        List<CompanyCoverRateInfoResponse> unitDensity;
        if (StringUtils.equals("yitu", source)){
            unitDensity = ytCoverageService.getCompanyCoverRates(companyId, currentCycleNum);
        }else {
            unitDensity = companyService.getCompanyCoverRates(companyId, currentCycleNum);
        }

        return new ResponseEntity<>(unitDensity, HttpStatus.OK);
    }

    /**
     * 手动补点: 标段覆盖情况查询
     *
     * @param manualFixRequest
     * @return
     */
    @PostMapping("/coverage/manualFix/query")
    public ResponseEntity<CompanyCoverRateInfoResponse> getUnitCoverRateInfo(@RequestBody ManualFixRequest manualFixRequest) {
        Optional<CompanyCoverRateInfoResponse> optional = companyService.getCompanyCoverRates(manualFixRequest.getCompanyId(), manualFixRequest.getCycleNum()).stream()
                .filter(item -> item.getUnitId().equals(manualFixRequest.getUnitId()))
                .findFirst();
        if (optional.isEmpty()) {
            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
        } else {
            // 替换roads
            // 额外增加航塘公路的补点信息
            for (RoadCoverDetail road : optional.get().getRoads()) {
                if (road.getRoadName().equals("航塘公路")){
                    road.setStatus(RoadCoverDetail.RoadCoverageStatusEnum.AMENDMENT);
                    road.setWorkLength(1.751);
                    road.setWorkLengthAll(1.751);
                    road.setCoverRate("1.0");
                }
            }
            CompanyCoverRateInfoResponse companyCoverRateInfoResponse = optional.get();
            List<RoadCoverDetail> roads = companyCoverRateInfoResponse.getRoads()
                    .stream()
                    .filter(item -> ObjectUtils.isNotEmpty(item.getCoordinates()))
                    .collect(Collectors.toList());
            companyCoverRateInfoResponse.setRoads(roads);
            return new ResponseEntity<>(companyCoverRateInfoResponse, HttpStatus.OK);
        }
    }

    /**
     * 手动补点: 标段覆盖率一键补齐
     *
     * @param manualFixRequest
     * @return
     */
    @PutMapping("/coverage/gps/unit")
    public ResponseEntity<JSONObject> gpsAmendmentByUnit(@RequestBody ManualFixRequest manualFixRequest) {
        boolean flag = carService.checkGpsManual(manualFixRequest.getCarId());
        if (!flag) {
            return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
        }
        long now = System.currentTimeMillis() / 1000;
        GpsHistory lastOne = gpsHistoryDao.findOne(new Query(Criteria.where("car_id").is(manualFixRequest.getCarId())
                .and("timestamp").gt(now)).with(new Sort(Sort.Direction.DESC, "timestamp")).skip(0).limit(1));
        if (ObjectUtils.isEmpty(lastOne)) {
            lastOne = GpsHistory.builder().timestamp(now).build();
        }
        long timestamp = lastOne.getTimestamp() + 2;
        List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("unit_id").is(manualFixRequest.getUnitId()).and("coordinates").exists(true)));
        for (RoadInfo roadInfo : roadInfoList) {
            List<Coordinate> coordinates = roadInfo.getCoordinates();
            if (CollectionUtils.isEmpty(coordinates)) {
                log.info("道路: {}, 图层数据为空!!!", roadInfo.getId());
                continue;
            }
            System.out.println("采样前数据量: " + coordinates.size());
            coordinates = LngLonUtil.gpsSampling(coordinates, gpsFixDistance);
            System.out.println("采样后数据量: " + coordinates.size());
            for (Coordinate coordinate : coordinates) {
                int speed = RandomUtils.nextInt(10, 70);
                double longitude = Double.parseDouble(coordinate.getX());
                double latitude = Double.parseDouble(coordinate.getY());
                GpsHistory gpsHistory = GpsHistory.builder().carId(manualFixRequest.getCarId()).timestamp(timestamp).longitude(longitude)
                        .latitude(latitude).speed(speed).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                        .creationTime(new Date(timestamp * 1000)).id(manualFixRequest.getCarId() + timestamp).build();
                gpsHistoryDao.saveOrUpdate(gpsHistory);
                timestamp = timestamp + 2;
            }
        }
        companyService.refreshRoadCoverageWeekByFix(manualFixRequest.getUnitId(), null);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("status", "success");
        return new ResponseEntity<>(jsonObject, HttpStatus.OK);
    }

    /**
     * 手动补点: 道路覆盖率一键补齐
     *
     * @param manualFixRequest
     * @return
     */
    @PutMapping("/coverage/gps/road")
    public ResponseEntity<JSONObject> gpsAmendmentByRoad(@RequestBody ManualFixRequest manualFixRequest) {
        boolean flag = carService.checkGpsManual(manualFixRequest.getCarId());
        if (!flag) {
            return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
        }
        long now = System.currentTimeMillis() / 1000;
        GpsHistory lastOne = gpsHistoryDao.findOne(new Query(Criteria.where("car_id").is(manualFixRequest.getCarId())
                .and("timestamp").gt(now)).with(new Sort(Sort.Direction.DESC, "timestamp")).skip(0).limit(1));
        if (ObjectUtils.isEmpty(lastOne)) {
            lastOne = GpsHistory.builder().timestamp(now).build();
        }
        long timestamp = lastOne.getTimestamp() + 2;
        List<RoadInfo> roadInfoList = roadInfoDao.findByQuery(new Query(Criteria.where("_id").is(manualFixRequest.getRoadId())));
        for (RoadInfo roadInfo : roadInfoList) {
            List<Coordinate> coordinates = roadInfo.getCoordinates();
            if (CollectionUtils.isEmpty(coordinates)) {
                log.info("道路: {}, 图层数据为空!!!", roadInfo.getId());
                continue;
            }
            System.out.println("采样前数据量: " + coordinates.size());
            coordinates = LngLonUtil.gpsSampling(coordinates, gpsFixDistance);
            System.out.println("采样后数据量: " + coordinates.size());
            for (Coordinate coordinate : coordinates) {
                int speed = RandomUtils.nextInt(10, 70);
                double longitude = Double.parseDouble(coordinate.getX());
                double latitude = Double.parseDouble(coordinate.getY());
                GpsHistory gpsHistory = GpsHistory.builder().carId(manualFixRequest.getCarId()).timestamp(timestamp).longitude(longitude)
                        .latitude(latitude).speed(speed).location(Arrays.asList(longitude, latitude)).isSupplement(Boolean.TRUE)
                        .creationTime(new Date(timestamp * 1000)).id(manualFixRequest.getCarId() + timestamp).build();
                gpsHistoryDao.saveOrUpdate(gpsHistory);
                timestamp = timestamp + 2;
            }
        }
        companyService.refreshRoadCoverageWeekByFix(manualFixRequest.getUnitId(), manualFixRequest.getRoadId());
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("status", "success");
        return new ResponseEntity<>(jsonObject, HttpStatus.OK);
    }

    /**
     * 获取养护公司信息
     *
     * @return
     */
    @GetMapping("/company/query")
    public ResponseEntity<List<Company>> getCompanyInfo() {
        final List<Company> all = companyDao.findAll();
        return new ResponseEntity<>(all, HttpStatus.OK);
    }

    @GetMapping("/company/coverRate/total")
    public ResponseEntity<List<CoverRateAll>> getCoverRateTotal(@RequestParam(required = false) String source) {
        //获取当前用户的角色
        Set<String> companyIdSet = companyDao.findAll().stream().map(Company::getId).collect(Collectors.toSet());
        UserDTO userDTO = userService.queryUserByUsername(SecurityUtils.getCurrentUser().getUsername());
        Set<String> roleIdList = userDTO.getRoleIdList();
        List<RoleDTO> roleDTOS = roleService.queryRolesById(roleIdList);
        List<String> roleName = roleDTOS.stream().map(RoleDTO::getRoleName).collect(Collectors.toList());
        Query queryCoverage = new Query();
        if (!roleName.contains("管理员") && !roleName.contains("道运中心") && !roleName.contains("市道运局")) {
            queryCoverage.addCriteria(Criteria.where("car_id").in(userDTO.getCarIdList()));
            List<Car> cars = carDao.findByQuery(new Query(Criteria.where("_id").in(userDTO.getCarIdList())));
            if (ObjectUtils.isNotEmpty(cars)) {
                companyIdSet = companyIdSet.stream().filter(companyId -> companyId.equals(cars.get(0).getHandleCompany())).collect(Collectors.toSet());
            }
        }
        List<CoverRateAll> coverRateTotal = companyService.getCoverRateTotal(companyIdSet);
        return new ResponseEntity<>(coverRateTotal, HttpStatus.OK);
    }

    @GetMapping("/coverage/cycleNum")
    public ResponseEntity<List<CycleNumVO>> getCycleNumList() {
        List<CycleNumVO> res = new ArrayList<>();
        Date monthAgo = DateUtils.addDays(new Date(), -30);
        String monthAgoStr = MyDateUtil.dateFormatStr(monthAgo, "yyyy-MM-dd");
        List<CycleNum> cycleNumList = cycleNumDao.findByQuery(new Query(Criteria.where("_id").gte(monthAgoStr)));
        cycleNumList.stream()
                .sorted(Comparator.comparing(CycleNum::getId))
                .collect(Collectors.groupingBy(CycleNum::getCycleNum))
                .forEach((id, list) -> {
                    CycleNumVO cycleNumVO = CycleNumVO.builder().cycleNum(id).dateList(list.stream().map(CycleNum::getId).collect(Collectors.toList())).build();
                    res.add(cycleNumVO);
                });
        List<CycleNumVO> sortedRes = res.stream().sorted(Comparator.comparing(CycleNumVO::getCycleNum)).collect(Collectors.toList());
        return new ResponseEntity<>(sortedRes, HttpStatus.OK);
    }

    //更新公司关键点位
    @GetMapping("/company/info2")
    public void updateCompanyInfo2() {
        companyService.updateCompanyInfo2();
    }

    //更新公司关键点位的gps坐标
    @GetMapping("/company/keyPointsGps")
    public void updateCompanyKeyPointsGps() {
        companyService.updateCompanyKeyPointsGps();
    }

    @GetMapping("/company/keyPointsGpsRoadName")
    public void updateGpsKeyPointsRoadName() {
        companyService.updateGpsKeyPointsRoadName();
    }
}
